
#include <stdio.h>

#include "decl.h"

Obj*
null (Obj *o)
{
    if (!o || o == Elist || o == pF)
        return pT;
    return pF;
}

Obj*
atom (Obj *o)
{
    if (o->t <= TList)
        return pT;
    return pF;
}

Obj*
eq (Obj *a, Obj *b)
{
    if (!a && !b)
        return pT;
    if (!a || !b)
        return pF;
    if (a->t == b->t)
        if (a->u.l == b->u.l)
            return pT;
    return pF;
}

Obj*
begin (int l, Obj **o)
{
    Obj *r = nil;
    int  i = 0;
    for (; i < l; i++)
        r = eval (o[i]);
    return r;
}

Obj*
cond (int l, Obj **o)
{
    Obj *h;
    int  i = 0;
    for (; i < l; i++) {
        h = eval (car (o[i]));
        if (isTrue (h)) 
            return eval (car (cdr (o[i])));
    }
    return nil;
}

Obj*
lambda (int l, Obj **o)
{
    Lambda  *f;
    Obj     *r;
    int      k, i;

    if ((l % 2) != 0) {
        printf ("ERROR LAMBDA DEF\n");
        return nil;
    }

    k = l / 2;

    f = malloc (sizeof (Lambda) + (sizeof (Obj*) * k));
    f->nc = k;
    f->na = len (o[0]);

    for (k = 0, i = 0; k < f->nc; k++, i += 2)
        /* TODO: check if all args are the same number */
        f->o[k] = cons (o[i], o[i+1]);

    r       = newObj ();
    r->t    = TLambda;
    r->u.p  = f;

    return r;
}

int
checkCondition (Obj *a, int l, Obj **o) 
{
    if (a) {
    Obj *x = car (a);
    if ((x->t == TSymbol) || (isTrue (eq (x, o[0]))))
        return 1 + checkCondition (cdr (a), l-1, o+1);
    }
    return 0;
}
 
int
pushArgs (Obj *o, int l, Obj **a)
{
    if (o) {
        Obj *x = car (o);
        int i = 0;
        if (x->t == TSymbol) {
            push (x->u.p, a[0]);
            i = 1;
        }
        return i + pushArgs (cdr (o), l-1, a+1);
    }
    return 0;
}

Obj*
applyLambda (Lambda *f, int l, Obj **o)
{
    int     i, j, k = 0;
    Obj     *r = nil;

    if (f->na != l) {
        printf ("ERROR LAMBDA EXPECT %d ARGS\n", f->na);
        return nil;
    }

    for (i = 0; i < f->nc; i++) {
        j = checkCondition (car (f->o[i]), l, o);
        if (j == f->na) { /* matched condition */
            k = pushArgs (car (f->o[i]), l, o);
            r = eval (cdr (f->o[i]));
            break;
        }
    }
    popN (k);

    return r;
}

Obj*
def (Obj *s, Obj *o)
{
    if (s->t == TSymbol) {
        addSym (s->u.p, eval (o));
        return o;
    }
    return o;
}

